package definition

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"path/filepath"
	"time"

	"gopkg.in/yaml.v3"
)

// Parser represents a workflow parser
type Parser struct {
	// registry is the function registry
	registry FunctionRegistry
}

// FunctionRegistry represents a function registry
type FunctionRegistry interface {
	// GetFunction gets a function by name
	GetFunction(name string) (interface{}, error)
	// RegisterFunction registers a function
	RegisterFunction(name string, function interface{}) error
}

// NewParser creates a new workflow parser
func NewParser(registry FunctionRegistry) *Parser {
	return &Parser{
		registry: registry,
	}
}

// ParseFile parses a workflow from a file
func (p *Parser) ParseFile(filePath string) (*Workflow, error) {
	// Read the file
	data, err := ioutil.ReadFile(filePath)
	if err != nil {
		return nil, fmt.Errorf("failed to read file: %w", err)
	}

	// Parse the workflow based on the file extension
	ext := filepath.Ext(filePath)
	switch ext {
	case ".json":
		return p.ParseJSON(data)
	case ".yaml", ".yml":
		return p.ParseYAML(data)
	default:
		return nil, fmt.Errorf("unsupported file format: %s", ext)
	}
}

// ParseJSON parses a workflow from JSON
func (p *Parser) ParseJSON(data []byte) (*Workflow, error) {
	// Parse the workflow
	workflow, err := FromJSON(data)
	if err != nil {
		return nil, fmt.Errorf("failed to parse JSON: %w", err)
	}

	// Validate the workflow
	err = workflow.Validate()
	if err != nil {
		return nil, fmt.Errorf("invalid workflow: %w", err)
	}

	// Validate functions
	err = p.validateFunctions(workflow)
	if err != nil {
		return nil, fmt.Errorf("invalid functions: %w", err)
	}

	return workflow, nil
}

// ParseYAML parses a workflow from YAML
func (p *Parser) ParseYAML(data []byte) (*Workflow, error) {
	// Parse the YAML
	var rawWorkflow map[string]interface{}
	err := yaml.Unmarshal(data, &rawWorkflow)
	if err != nil {
		return nil, fmt.Errorf("failed to parse YAML: %w", err)
	}

	// Convert to JSON
	jsonData, err := json.Marshal(rawWorkflow)
	if err != nil {
		return nil, fmt.Errorf("failed to convert YAML to JSON: %w", err)
	}

	// Parse the JSON
	return p.ParseJSON(jsonData)
}

// SaveToFile saves a workflow to a file
func (p *Parser) SaveToFile(workflow *Workflow, filePath string) error {
	// Validate the workflow
	err := workflow.Validate()
	if err != nil {
		return fmt.Errorf("invalid workflow: %w", err)
	}

	// Validate functions
	err = p.validateFunctions(workflow)
	if err != nil {
		return fmt.Errorf("invalid functions: %w", err)
	}

	// Convert to JSON or YAML based on the file extension
	ext := filepath.Ext(filePath)
	var data []byte
	switch ext {
	case ".json":
		data, err = workflow.ToJSON()
		if err != nil {
			return fmt.Errorf("failed to convert workflow to JSON: %w", err)
		}
	case ".yaml", ".yml":
		// Convert to JSON first
		jsonData, err := workflow.ToJSON()
		if err != nil {
			return fmt.Errorf("failed to convert workflow to JSON: %w", err)
		}

		// Parse JSON to map
		var rawWorkflow map[string]interface{}
		err = json.Unmarshal(jsonData, &rawWorkflow)
		if err != nil {
			return fmt.Errorf("failed to parse JSON: %w", err)
		}

		// Convert to YAML
		data, err = yaml.Marshal(rawWorkflow)
		if err != nil {
			return fmt.Errorf("failed to convert workflow to YAML: %w", err)
		}
	default:
		return fmt.Errorf("unsupported file format: %s", ext)
	}

	// Write to file
	err = ioutil.WriteFile(filePath, data, 0644)
	if err != nil {
		return fmt.Errorf("failed to write file: %w", err)
	}

	return nil
}

// validateFunctions validates the functions in the workflow
func (p *Parser) validateFunctions(workflow *Workflow) error {
	if p.registry == nil {
		// Skip validation if no registry is provided
		return nil
	}

	// Check all task steps
	for id, step := range workflow.Steps {
		if step.Config.Type == StepTypeTask && step.Task != nil {
			// Check if the function exists
			_, err := p.registry.GetFunction(step.Task.Function)
			if err != nil {
				return fmt.Errorf("function %s not found for task step %s: %w", step.Task.Function, id, err)
			}
		}
	}

	return nil
}

// DefaultFunctionRegistry represents a default function registry
type DefaultFunctionRegistry struct {
	// functions is the function map
	functions map[string]interface{}
}

// NewDefaultFunctionRegistry creates a new default function registry
func NewDefaultFunctionRegistry() *DefaultFunctionRegistry {
	return &DefaultFunctionRegistry{
		functions: make(map[string]interface{}),
	}
}

// GetFunction gets a function by name
func (r *DefaultFunctionRegistry) GetFunction(name string) (interface{}, error) {
	function, ok := r.functions[name]
	if !ok {
		return nil, fmt.Errorf("function %s not found", name)
	}
	return function, nil
}

// RegisterFunction registers a function
func (r *DefaultFunctionRegistry) RegisterFunction(name string, function interface{}) error {
	if _, ok := r.functions[name]; ok {
		return fmt.Errorf("function %s already registered", name)
	}
	r.functions[name] = function
	return nil
}

// ParseDuration parses a duration string
func ParseDuration(durationStr string) (time.Duration, error) {
	return time.ParseDuration(durationStr)
}

// FormatDuration formats a duration
func FormatDuration(duration time.Duration) string {
	return duration.String()
}

// LoadWorkflow loads a workflow from a file
func LoadWorkflow(filePath string) (*Workflow, error) {
	// Create a parser
	parser := NewParser(nil)

	// Parse the workflow
	return parser.ParseFile(filePath)
}

// SaveWorkflow saves a workflow to a file
func SaveWorkflow(workflow *Workflow, filePath string) error {
	// Create a parser
	parser := NewParser(nil)

	// Save the workflow
	return parser.SaveToFile(workflow, filePath)
}

// CreateExampleWorkflow creates an example workflow
func CreateExampleWorkflow() *Workflow {
	// Create a workflow
	workflow := NewWorkflow(
		"example-workflow",
		"Example Workflow",
		"1.0.0",
		WorkflowTypeSequential,
		WorkflowModeStream,
	)

	// Add description
	workflow.Metadata.Description = "This is an example workflow"

	// Add steps
	step1 := NewTaskStep("step1", "Step 1", "exampleFunction1")
	step1.SetInput("param1", "value1")
	step1.SetOutput("result", "$.result")
	workflow.AddStep(step1)

	step2 := NewTaskStep("step2", "Step 2", "exampleFunction2")
	step2.SetInput("param1", "$.step1.result")
	step2.SetOutput("result", "$.result")
	step1.SetNextStep("step2")
	workflow.AddStep(step2)

	condition := NewConditionStep("condition", "Condition", "$.step2.result > 10", "step3", "step4")
	step2.SetNextStep("condition")
	workflow.AddStep(condition)

	step3 := NewTaskStep("step3", "Step 3", "exampleFunction3")
	step3.SetInput("param1", "$.step2.result")
	workflow.AddStep(step3)

	step4 := NewTaskStep("step4", "Step 4", "exampleFunction4")
	step4.SetInput("param1", "$.step2.result")
	workflow.AddStep(step4)

	// Set the start step
	workflow.SetStartStep("step1")

	return workflow
}

// CreateExampleWorkflowFile creates an example workflow file
func CreateExampleWorkflowFile(filePath string) error {
	// Create an example workflow
	workflow := CreateExampleWorkflow()

	// Save the workflow
	return SaveWorkflow(workflow, filePath)
}
